#include "../include/kasm.h"
#include "../include/defs.h"
#include "../include/teclado.h"
#include "../include/bufferTeclado.h"
#include "../include/buffer.h"
#include "../include/video.h"
#include "../include/mouse.h"
#include "../include/protpant.h"
#include "../include/kernel.h"
#include "../include/sched.h"
#include "../include/kc.h"
#include "../include/string.h"
#include "../include/paginacion.h"

#define MAX(a,b) ((a)>(b))?(a):(b)
#define CMDCANT 13
void shell(void);

DESCR_INT idt[0x100];			/* armo una idt con un vector de 10 vectores de interrupcion*/
IDTR idtr;				/* IDTR */
DESCR_SEG static gdt[256] = {{0}};

vid * vidmem1 =(vid *) VIDMEM;
	vid * vidmem2 =(vid *) VIDMEM;
	vid * vidmem3 =(vid *) VIDMEM;
	vid * vidmem4 =(vid *) VIDMEM;


int CurrentPID=0;
static int nextPID=1;
PROCESS procesos[64];
PROCESS idle;
TTY tty[8];

/* Memoria del backup de video */
static int shift = 0;
static int caps = 0;
//static int cantidad = 0;
//static unsigned char mouse_bytes[3];
//static int x=0;
//static int y=0;

static char * cmdlist[] = { "elp", "Protector" , "Llenar", "Logo","Prottime","Clear", "Formato","Mouse", "Echo", "Credits", "AddSegment", "ZeroDivide", "PrintGDT"};

int GetPID(void);
PROCESS* GetProcessByName (char* proceso);

int tickpos=640;

static int
isAlphanum(int c)
{
	return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
		(c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' || c == '~' || c == '!' || c == '@' || c == '#' || c == '$' ||c ==  '%' ||c == '^' ||c ==  '&' ||c == '*' ||c == '(' ||c == ')' ||c == '_' ||c == '+' ||c == '<' ||c == '>' || c =='?' ||c == '"' ||c == '|' ||c == '}' ||c == '_'||c == '-' ||c == ':' ||c == ';' ||c == ',' ||c == '.' ||c == '+' ||c == '*' ||c == '['  );
}

/*
void escribirLogo(void ){

	char * st = {"                     (`'`'`'`'`)\n                     |       |\n                     |       |\n                     |       |\n    -----..        (()----   |\n   |        ||     (_        |\n   |        ||       |       |\n   |        ||       |       |\n   |        ||       /\\   ..--\n   '--------''   /\\  ||-''    \\ \n      /   \\      \\ \\//   ,,   \\---. \n   .---------.    \\./ |~| /__\\  \\  |\n___|_________|__|""-.___ / ||   |  |\n|               | .-----'  ||   |  |\n|               |CC.-----.      |  |\n|               |  '-----'      |  |\n                                |  |\n	"}	;
	print(st);
}
*/

void escribirLogo(void ){
int form ;
	char * st = {"                                                                             \n                                                                             \n            """"tt;;          ;;  ;;;;;;;;;;;;  ;;;;;;;;        ;;;;         \n        ""kkJJkkkkZZJJ""    kkRRtt RRZZZZZZRR  RRZZRRZZZZ      RRZZww        \n        ZZ""ttkkkkwwkkZZ    JJZZtt ttZZZZJJtt  ZZZZ  kkZZ""  JJZZZZZZ        \n      RRkk;;kkJJJJkkwwkkRR  JJZZtt    ZZZZ      ZZZZwwZZZZ;;  ZZZZttZZkk     \n    ;;JJJJww""    ;;wwkkJJ  JZZttt    ZZZZ     ZZZZ  JJZZkkttZZZZZZZZZZ      \n    ttZZZZkk        kkZZZZt JJZZtt    ZZZZ     ZZZZZZZZww  ZZZZ    ttZZJJ    \n     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; \n	 I N S T I T U T O   T E C N O L O G I C O  DE   B U E N O S   A I R E S \n     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; \n"};
form = getDefaultFormat();
setDefaultFormat(0x79);
	print(st);
setDefaultFormat(form);

}



void docmd(int i,const char* param){
int form ;
/* Switch para ejecutar un comando */
	int l=0;
	int j=0;
	int k=0;
	char param1[5][100];
	for( l=0; l<4; l++){
		for(k=0; k<10; k++)
			param1[l][k]='\0';
	}
	l=0;
	k=0;

	switch(i){

		case 0:
		if (param[0] == '\0')
		{
			form = getDefaultFormat();
			setDefaultFormat(0x0c);
			print("\nHelp: Commands avaliable:"
					"\n    Llenar: fills the scren with text."
					"\n    Logo: prints the aplication logotipe."
					"\n    Prottime NumberOfSegs: Set the time to wait for the screen saver."
					"\n    Clear: cleans the screen."
					"\n    Formato: changes the default format to the specified."
					"\n    Echo: prints the string."
					"\n    Credits: Show the programmers name."
					"\n    AddSegment N BASE LIMIT DPL TYPE: Adds Segment in GDT."
					"\n    ZeroDivide: throws zero-divide exception."
					"\n    PrintGDT N: Prints the segment number N from the GDT."
					"\n");
			setDefaultFormat(form);
		}
		else
			print("Invalid Argument\n");
			break;
	/*	case 1:
		protpantenable();

		break;*/
		case 2:
		print("The text modes use memory in 4 80x25 or 8 40x25 character pages starting at B800h:0000h. Page size for high resolution text can be calculated as follows:\n* Each character has an 8-bit character code and an 8-bit color code\n* Each character therefor uses 2 bytes\n* Characters are stored one full row after another tightly packed\n* ( 2 bytes/character * 80 characters/row * 25 rows/page ) = 4000 bytes/page.\n* Therefor the first page, page 0, goes from B800h:0000h => B800h:0F9Fh \n B800:0000 stores the ASCII code of the character at row-0 and column-0 \n B800:0001 stores the attribute of the character \n B800:0002 stores the ASCII code of the character at row-0 and column-1 \n B800:0003 stores the attribute of the character\n              **********      \n\nThe text modes use memory in 4 80x25 or 8 40x25 character pages starting at B800h:0000h. Page size for high resolution text can be calculated as follows:\n* Each character has an 8-bit character code and an 8-bit color code\n* Each character therefor uses 2 bytes\n* Characters are stored one full row after another tightly packed\n* ( 2 bytes/character * 80 characters/row * 25 rows/page ) = 4000 bytes/page.\n* Therefor the first page, page 0, goes from B800h:0000h => B800h:0F9Fh \n B800:0000 stores the ASCII code of the character at row-0 and column-0 \n B800:0001 stores the attribute of the character \n B800:0002 stores the ASCII code of the character at row-0 and column-1 \n B800:0003 stores the attribute of the character\n");

		break;
		case 3:
		if (param[0] == '\0')
		{
			escribirLogo();
		}
		else
			print("Invalid Argument\n");
		case 4:
		i = atoi(param);
		i = i % 3600;
		setProtPantTime(i);
		break;

		case 5:
		if (param[0] == '\0')
		{
			clearScreen();
		}
		else
			print("Invalid Argument\n");
		break;
		case 6:
		i = atoi(param);
		i = i % 0xFF;
		setDefaultFormat(i);
		print("\n");
		break;

		case 7:
		i = atoi(param);

		i = i % 50;
		Adjustmouse(i);
		break;

		case 8:
		form = getDefaultFormat();
		setDefaultFormat(0x0A);
		print((char *) param);
		setDefaultFormat(form);
		print("\n");
		break;

		case 9:
			if (param[0] == '\0')
			{
				print("\n");
				form = getDefaultFormat();
				setDefaultFormat(0x4B);
				print("Daniel Goldberg\nCristian Prieto\nM. Eugenia Cura\n");
				setDefaultFormat(form);
				print("\n");
			}
			else
				print("Invalid Argument\n");
			break;

		case 10:
			if (param[0] != '\0')
			{
				while(param[l]!='\0')
				{
					while(param[l]!= ' ' && param[l] != '\0')
					{
						param1[j][k]=param[l];
						k++;
						l++;
					}
					l++;
					k=0;
					j++;
				}
				if (j!=5)
				{
					print("Invalid Arguments\n");
				}
				else{
					if(strCmp(param1[0], "0") ||
							strCmp(param1[0], "1") ||
							strCmp(param1[0], "2") ||
							strCmp(param1[0], "3") ||
							strCmp(param1[0], "4"))
					{
						print("Segmento reservado para grub\n");
					}
					else{
						if((atoi(param1[3])>=0 && atoi(param1[3])<4)&& //dpl
							(atoi(param1[4])>=0 && atoi(param1[3])<8) //tipo
							)
						{

							unsigned long base = atol(param1[1]);
							unsigned long limite = atol(param1[2]) & 0x0ffff;
							unsigned char access = ((atoi(param1[3]) << 1 | 9 ) << 4) | (atoi(param1[4]) << 1 | 0);
							unsigned char atrib = ((atoi(param1[2]) >> 16) & 15) |( 8 << 4 );

							gdt_set_gate(&gdt[atoi(param1[0])], base, limite, access, atrib);
							print("Segmento agregado correctamente\n");
						}
						else{
							print("Error en seteo de permisos o tipo\n");
							print("		DPL debe ser un numero entre 0 y 3\n");
							print("		TIPO debe ser un numero entre 0 y 7\n");
						}
					}
				}
			}
			else
				print("Invalid Argument\n");
			break;

		case 11:
			daError();
			break;
		case 12:
			if (param[0] != '\0'){
				print("\n");
				printGDT(atoi(param));
				print("\n");
			}

	}
}


static char param[2000];
static void
command( char * st )
{
	int i = 0, espacioflag = 0;
	int j = 0;
	newLineCursor();
	param[0] = '\0';
	while(st[i] != '\0')
	{
		if(espacioflag == 1)
		{
			param[j] = st[i];
			j++;
		}
		if(st[i] == ' ' && espacioflag!=1)
		{
		  espacioflag = 1;
		  st[i] = '\0';
		}
		i++;
	}
	param[j]='\0';

	/*decide que comando es*/
	for( i= 0; i < CMDCANT; i++)
	{
		if (strCmp(st, cmdlist[i]))
		{
			docmd(i,param);

			for (i = 0 ; i < 1999 ; i++)
				param[i] = '\0';
			return;
		}
	}

	for (i = 0 ; i < 1999 ; i++)
		param[i] = '\0';
	print("Invalid command\n");
	limpiarTab(vidmem2);
	LoadVideo(vidmem2);

}



void
int_09(unsigned char scancode)
{
	protpantdisable();

	if (scancode & MASK_RELEASE)
	{
		if (IS_SHIFT(scancode & MASK_SCAN))
			shift = 0;
	/*	else
			tickpos +=2;
*/
		return;
	}
	else if (IS_SHIFT(scancode))
			shift = 1;
	else if(IS_CAPS(scancode))
	{
		if (caps == 1)
			caps = 0;
		else
			caps = 1;

	}
	/*
	 * Si es CAPS invierto el flag.
	 * Si es un SHIFT prendo el flag.
	 * Si es BACKSPACE retrocedo el cursor si tengo algo en el buffer,
	 * lo saco del buffer (para no borrar el prompt).
	 * Si es SPACE avanzo el cursor e inserto un space en el buffer.g
	 * Si es ENTER voy a la siguiente linea, y le indico a la libreria
	 * de buffer que prepare el buffer para ser leido.
	 * Si no tengo la tecla en el mapa la ignoro (se puede extender definiendo
	 * un valor no nulo en keymap para el respectivo scancode).
	 * Si la tecla esta mapeada, se imprime e inserta en el buffer.
	 */
	else if (IS_BS(scancode))
	{
		InsertarTecladoKey(8);
	}
	else if (IS_SPACE(scancode))
	{
		InsertarTecladoKey(' ');

	}
	else if (IS_ENTER(scancode))
	{
		InsertarTecladoKey('\n');
	}
	else
	if (mapkey[ (!shift && caps) || (shift && !caps )][scancode] != 0)
	{
	InsertarTecladoKey(mapkey[(!shift && caps) || (shift && !caps )][scancode]);
	}
}

void prompt(){
	print("Ingresar Comando: ");
}


//Setea el segmento
void gdt_set_gate( DESCR_SEG * item, unsigned long base, unsigned long limit,unsigned char access, unsigned char attr)
{
	item->base_l = (base & 0xFFFF);
	item->base_m = (base >> 16) & 0xFF;
	item->base_h = (base >> 24) & 0xFF;

	item->limit = (limit & 0xFFFF);
	item->attribs = ((limit >> 16) & 0x0F);

	item->attribs |= (attr & 0xF0);
	item->access = access;
}

GDTR miGDT;

void
printGDT(int i){
	if(i<0 || i>255)
		return;
	long base = (gdt[i].base_h << 24) | (gdt[i].base_m << 16) | gdt[i].base_l;
	//gdt[i].attribs;
	printf("posicion: %d", i);
	print("\n");
	printf("base: %x", base);
	print("\n");
	printf("limite: %l", gdt[i].limit);
	print("\n");
}

void
cargaGDT(){
	int i=0;
	_sgdt(&miGDT);
	DESCR_SEG * descGrub = (DESCR_SEG*)miGDT.base;
	long base;
	descGrub++;
	for(i=1; i<5; i++)
	{
		base = (descGrub->base_l << 24) & (descGrub->base_m << 16) & descGrub->base_h;
		gdt_set_gate(&gdt[i], base, descGrub->limit, descGrub->access, descGrub->attribs);
		descGrub++;
	}

	miGDT.base = (dword)&gdt;
	miGDT.limit = 256*8;
	_lgdt(&miGDT);

}

/**********************************************
kmain()
Punto de entrada de cóo C.
*************************************************/


void kmain(void)
{
/* CARGA DE IDT CON LA RUTINA DE ATENCION DE IRQ0    */

	setup_IDT_entry (&idt[0x20], 0x08, (dword)&_int_08_hand, ACS_INT, 0);
	setup_IDT_entry (&idt[0x21], 0x08, (dword)&_int_09_hand, ACS_INT, 0);
	//setup_IDT_entry (&idt[0x74], 0x08, (dword)&_int_74_hand, ACS_INT, 0);
	setup_IDT_entry (&idt[0x80], 0x08, (dword)&_int_80_hand, ACS_INT, 0);
	setup_IDT_entry (&idt[0x0], 0x08, (dword)&_excp_0_hand, ACS_INT, 0);
	setup_IDT_entry (&idt[0x4], 0x08, (dword)&_excp_04_hand, ACS_INT, 0);
	setup_IDT_entry (&idt[0x6], 0x08, (dword)&_excp_06_hand, ACS_INT, 0);

/* Carga de IDTR    */

	idtr.base = 0;
	idtr.base +=(dword) &idt;
	idtr.limit = sizeof(idt)-1;

	_lidt (&idtr);

/* Inicializo los pics */
	inicializarPics(0x20,0x28);

	_Cli();

	// Inicializa el estado de las paginas para los procesos
	initPageStatus();

	// Activa el sistema de paginacion
	initPaging();

	// Inicia la configuracion para el uso de memoria dinamica. Solo
	// hay disponibles 4 MB y solo el kernel puede pedirla. Los procesos
	// usan pmalloc y pfree respectivamente para manejo de memoria
	//initFileSysMem();


/* Habilito interrupcion de timer tick*/
	_mascaraPIC1( 0xF8 );
	_mascaraPIC2( 0xEF );

	_Sti();

	cargaGDT();

/* Borra la pantalla. */

	clearScreen();


	prompt();

	shell();
}

void 
shell(void){
		int c, i;// j;
		char output[2000];
		//int flag;
		//int cant = 0;
		while(1)
		{
			if ( !BufferTecladoEmpty() )
			{
				c = getchar();
				if(  c == 8 ||  isAlphanum(c) || c == '\n' ||  c== ' ')
				{
					if (c == 8)
					{
						if (!BufferIsEmpty())
						{
							RwdCursor();
							putchar(' '); /*Back space*/
							RwdCursor();
							RemoveKey();
						}
					}
				if ( !BufferIsFull())
				{
					if (c != 8)
						{
							if(c != '\n')
								putchar(c);
							InsertKey(c);
						}
					if (BufferIsReady() )
					{
						i = FlushBuffer(output);

						command(output);
						prompt();
					}
					else
					;
				}
				else {
					print("Buffer Full\n");
					FlushBuffer(output);
					command(output);
					prompt();

				}
				}
			}
		}
}

int LoadStackFrame(int(*process)(int,char**),int argc,char** argv, int bottom, void(*cleaner)());

//Retorna un proceso a partir de su PID
PROCESS* GetProcessByPID (int pid)
{
	//char* video=(char*)0xb8000;
	int i;
	//video[2080+contador*2]=pid+0x30;
	
	if (pid==0)
	{
		return &idle;
	}
	for(i=0;i<64;i++)
	{
		if (procesos[i].pid== pid)
		{
			return &procesos[i];
		}
	}
	return 0;
}

//Funcion que crea procesos
void CreateProcessAt(char* name, int (*process)(int,char**),int tty, int argc, char** argv, int stacklength, int priority, int isFront)
{
	//PROCESS* newprocess;
	//char* video =(char*) 0xb8000;
	int i;
	void* stack=(void *)Malloc(stacklength);
	for(i=0;i<64;i++)
	{
		if(procesos[i].free==1)
			break;
	}
	procesos[i].pid=GetPID();
	procesos[i].foreground=isFront;
	procesos[i].priority=priority;
	memcpy2(procesos[i].name,name,strlen2(name)+1);
	procesos[i].sleep=0;
	procesos[i].blocked=0;
	procesos[i].tty=tty;
	procesos[i].lastCalled=0;
	procesos[i].stacksize=stacklength;
	procesos[i].stackstart=(int)stack;
	procesos[i].free=0;
	procesos[i].ESP=LoadStackFrame(process,argc,argv,(int)(stack+stacklength-1),Cleaner);
	procesos[i].parent=0;
	if(isFront && CurrentPID>=1)
	{
		PROCESS* proc=GetProcessByPID(CurrentPID);
		proc->blocked=2;
		procesos[i].parent=CurrentPID;
	}
	//video[2080]=procesos[i].pid+0x30;
	
}


//Funcion que arma los stack frames de cada proceso
int LoadStackFrame(int(*process)(int,char**),int argc,char** argv, int bottom, void(*cleaner)())
{
	STACK_FRAME* frame= (STACK_FRAME*)(bottom-sizeof(STACK_FRAME));
	frame->EBP=0;
	frame->EIP=(int)process;
	frame->CS=0x08;
	
	frame->EFLAGS=0;
	frame->retaddr=cleaner;
	frame->argc=argc;
	frame->argv=argv;
	return (int)frame;
}


#define PIC1 0x20
#define PIC2 0xA0

void inicializarPics(int remPic1, int remPic2)
{
	/* trabajan en cascada */
	outportb(PIC1, 0x11);
	outportb(PIC2, 0x11);
	/* indica donda mapear el pic 1 y el pic 2 */

	outportb(PIC1 + 1, remPic1);	/* remap */
	outportb(PIC2 + 1, remPic2);	/*  pics */
	/* indica cual IRQ conecta a un pic con el otro */
	outportb(PIC1 + 1, 4);
	outportb(PIC2 + 1, 2);

	/* send ICW4 */
	outportb(PIC1 + 1, 0x01);
	outportb(PIC2 + 1, 0x01);

	/* disable all IRQs */
	outportb(PIC1 + 1, 0xFF);

}

PROCESS* GetProcessByName (char* proceso)
{
	int i, longitud;
	
	if (memcmp2(proceso, "Idle", 4))
	{
		return &idle;
	}
	longitud = strlen2(proceso);
	for(i=0;i<64;i++)
	{
		if (memcmp2(proceso, procesos[i].name, MAX(longitud, strlen2(procesos[i].name))))
		{
			return &procesos[i];
		}
	}
	return 0;
}


int GetPID(void)
{
	return nextPID++;
}
